use std::pin::Pin;

use actix_web::{web::Path, FromRequest};
use futures::Future;
use serde::{Deserialize, Serialize};
use wither::bson::oid::ObjectId;

use super::strings::string_to_object_id;

/// ## PathParams
/// 路径参数大全。
/// 用法
/// ```rust
/// async fn get_param(path_params:web::Path<PathParams>) -> AppResult<HttpResponse>;
/// ```
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PathParams {
    pub club_id: Option<String>,
    pub user_id: Option<String>,
    pub assets_id: Option<String>,
    pub form_id: Option<String>,
    pub application_id: Option<String>,
    pub invite_id: Option<String>,
    pub message_id: Option<String>,
}

/// ## PathParamMDB
/// 统一转换到ObjectId
/// ```rust
/// PathParamMDB::from(path_params)
/// ```
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PathParamMDB {
    pub club_id: Option<ObjectId>,
    pub user_id: Option<ObjectId>,
    pub assets_id: Option<ObjectId>,
    pub form_id: Option<ObjectId>,
    pub application_id: Option<ObjectId>,
    pub invite_id: Option<ObjectId>,
    pub message_id: Option<ObjectId>,
}

impl From<PathParams> for PathParamMDB {
    fn from(p: PathParams) -> Self {
        Self {
            club_id: p.club_id.and_then(string_to_object_id),
            user_id: p.user_id.and_then(string_to_object_id),
            assets_id: p.assets_id.and_then(string_to_object_id),
            form_id: p.form_id.and_then(string_to_object_id),
            application_id: p.application_id.and_then(string_to_object_id),
            invite_id: p.invite_id.and_then(string_to_object_id),
            message_id: p.message_id.and_then(string_to_object_id),
        }
    }
}
impl FromRequest for PathParamMDB {
    type Error = actix_web::Error;
    type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>;
    fn from_request(
        req: &actix_web::HttpRequest,
        payload: &mut actix_http::Payload,
    ) -> Self::Future {
        let params = Path::<PathParams>::from_request(req, payload);
        Box::pin(async move {
            let res = params.await;
            match res {
                Ok(o) => Ok(PathParamMDB::from(o.into_inner())),
                Err(e) => Err(e),
            }
        })
    }
}
